feat: complete Go/Wails → C++/WebView migration#26
Merged
Conversation
Contributor
|
Contributor
|
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool. What Enabling Code Scanning Means:
For more information about GitHub Code Scanning, check out the documentation. |
saveLayoutToStorage() serializes the pane tree with tab INDICES (not IDs) so it survives sessions where tabs get fresh IDs. restoreLayoutFromStorage() maps indices back to the newly-restored tab IDs. Layout + focused pane are restored inside the soft-close session-restore path.
… during sub-page navigation - Remove standalone 36px header bar; window controls move to the focused pane's tab bar via a windowControls prop threaded through SplitPaneView - PaneTabBar accepts windowControls slot rendered after the pane controls - Remove split-menu dropdown (PaneTabBar already has H/V split buttons) - renderContent now always mounts terminals (display:none when not visible) so sub-page navigation no longer unmounts/gray-screens the terminal layer
… on window controls
…te separator conditional
Add 'ports' to PageId, wire Sidebar button to onNavigate('ports'),
and render PortsTab in renderSidebarPage alongside other sidebar pages.
…n loss Iterate tabs (not leaves) in the terminal overlay so orphaned terminals stay mounted with display:none during the brief gap between pane removal and tab reassignment, preventing CloseTerminal from destroying the session.
Implements git.status, git.diff, git.add, git.reset, git.discard, git.stash, git.stash.list, git.stash.pop, git.stash.drop, git.commit, git.push, git.pull, git.branches, git.checkout. Uses CreateProcess (Windows) / popen (POSIX) with no console window. Commit messages written to temp files to avoid command-line quoting issues with embedded quotes and newlines.
Built fresh, not derived from the deleted implementation: jobs are grouped into dependency-depth lanes and rendered as content-sized pill elements in normal document flow (CSS flex, not a hand-rolled pixel grid). Connector positions come from actual measured DOM rects via ResizeObserver, and wires are only ever drawn between a lane and the one immediately before it. There's no separate outcome column at all — a terminal job carries its own pass/fail marker directly — so the long cross-column connector that used to cut through unrelated nodes can't occur structurally. A dependency on a non-adjacent lane is called out as text under the job instead of a drawn line.
Shape (999px pill radius) and orientation (left-to-right lanes) were both rejected. Removing this version entirely before rebuilding top-to-bottom with a different node shape, rather than tweaking the existing pills/orientation in place.
Built from a blank file, not adapted from the deleted pill version: flows top to bottom instead of left to right, cards are rectangles with a sane 6px corner radius and a colored top accent border instead of 999px pill capsules, and rows are layered with Kahn's algorithm (peel off the current frontier, repeat) instead of the memoized longest-path recursion the previous two versions both used under the hood. Same structural guarantee as before: a terminal job carries its own pass/fail chips, so there's no distant outcome row for a wire to cross behind another card to reach.
Jobs and steps now carry their source line numbers so each card's edit pencil and per-job "Add step" button jump straight to the right spot in the code editor. Terminal jobs show real pass/fail/running status from the latest run instead of static placeholder chips, and every step lists its own outcome dot.
Lets users create new jobs directly from the events map, link two jobs by clicking connector dots on their cards (writing a needs: edge), and set or cycle a pass/fail/custom run condition on each link. Edits are spliced directly into the workflow YAML on disk via the generic fs.writefile channel, preserving unrelated file content untouched.
Contributor
🔧 Auto-fixed ESLint issues — app/frontendThe |
window.prompt() doesn't match the app's UI at all. Adds a styled modal (same look as the split-view dialog) for entering a custom run condition expression when cycling a link to "Other".
setJobCondition only replaced the if: line itself, leaving a block scalar's (if: >- / if: |) continuation lines behind as orphaned text that broke the file's YAML. Parses and replaces the whole ifLine.. ifEndLine span instead.
Combine the two sub-tabs into one "Code" section with the code editor on the left and the events map on the right, sharing one toolbar with Add process and Edit Workflow side by side. The divider between panes is drag-resizable. Also adds drag-to-pan inside the events map canvas so large workflows can be navigated like a map instead of relying on scrollbars; the old full-width toolbar there is replaced by a small floating hint while linking two processes.
Contributor
🔧 Auto-fixed ESLint issues — app/frontendThe |
The code editor was getting squeezed at an even 50/50 split. Give it more room by default and shrink the events map's starting share.
Drop the redundant path label from the code/events bar and put the section tabs there instead, since the path is already visible in the code pane. The Run button moves down next to Add process and Edit Workflow so every action lives in a single bar above the content.
Clicking Run now switches to the Console tab so the live output is immediately visible, and Add process switches to the Code tab so the newly inserted job is visible right away.
The workflow code pane was always rendering with default font size and no indent guides regardless of the user's editor settings. Wire appConfig.indent_guides and the current zoom level through, same as the main Editor component. Minimap is intentionally left out since it's too heavy for a small code snippet pane.
…p dot alignment Console tab now shows live output and the step checklist side by side, checklist narrower on the right, mirroring the Code tab's split layout. Step status dots were vertically centered against the row's full stretched height, which includes the bottom spacer between rows. That drifted the dot away from the step title whenever the title wrapped to multiple lines. Anchor the title's first line to the dot's center instead via a fixed line-height and padding-top.
The console output and background process list already surface the run's result, so the pill next to the Run button was redundant.
Hoist the sortable column header component to module scope so it keeps a stable identity across re-renders, memoize the filtered/sorted derivations, and fix row keys to include pid + index instead of just protocol:address:port. That composite alone collides heavily on real systems (multiple connections sharing a local port), and duplicate React keys were causing the table to accumulate stale rows on every poll tick instead of replacing them, which made sorting look like it stopped working after the first click.
…buttons Add a shared SortableColumnHeader component: clicking a column header now opens a dropdown with explicit ascending/descending sort options and per-table column visibility toggles. Ports' Port and State headers fold their protocol/state filters into the dropdown, replacing the standalone select boxes that used to sit next to the search bar. Endpoints gets the same sort+column-visibility menu on its headers.
Adds a machine-local relay (no router/UPnP/NAT changes) for forwarding
a listening port to another host:port reachable from this machine.
- cpp/src/port_forward.{hpp,cpp}: Winsock-based TCP/UDP relay manager,
threaded per rule, persisted via Config under "portForwards", with
start/stop lifecycle hooks wired into the Dispatcher.
- Frontend: new "Forwards" sub-tab on the Ports page listing configured
forwards with enable/disable/remove actions, plus a "New Forward"
modal for entering name, protocol, listen port, and target host:port.
Forwards now sits after Open Ports/Endpoints in the sub-nav. Replaced the inline em-dash disclaimer with a hoverable info icon and dropped em-dashes from the remaining copy.
Native title attribute had OS-controlled hover delay and forced a help cursor. Replaced with a CSS-only hover tooltip that shows immediately and uses the default cursor.
Rename all Command IDE / cmdIDE / terminal-IDE references to Binder across source, docs, CI workflows, build tooling, and Windows resource files. Publisher is now BinderTools, Task Manager identity comes from VERSIONINFO in resources.rc, and logo/icon/splash assets are replaced with the new mark. Submodules and remotes are repointed to the bindertools org.
Contributor
🔧 Auto-fixed ESLint issues — app/frontendThe |
- type problems.scan response via invoke<T> instead of misapplied as-cast - remove unused textToB64 import and dead prevVisibleLine helper - mark unused destructured/parameter bindings with underscore prefix - void the fire-and-forget window.close invoke call
…t/webview-migration # Conflicts: # app/frontend/src/App.tsx
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
Step 2 of ripping Wails and Go out of cmdIDE and replacing them with a from-scratch C++ host wrapped around WebView2 (and
webview/webviewfor cross-platform parity). Step 1 (feat/cpp-migration) got the C++ backend compiling and talking to the existing Go process over a named pipe. This PR finishes the job: it builds the actual native window, wires up IPC end-to-end, ports every backend feature the frontend depends on, rebuilds the installer in C++/WebView too, and then deletes the entire Go/Wails codebase (129 files) once nothing depends on it anymore.Branch hierarchy:
main←feat/cpp-migration(Step 1) ←feat/webview-migration(Step 2, this PR)How it went
Getting a window on screen. Scaffolded
cpp/host/aroundwebview/webviewvia vcpkg. First milestone was a blank WebView2 window loadingwww/next to the exe, which took some iteration to get CMake copying the frontend'sdist/output into the right place at build time. Once that worked, registered__cmdide_invokeas the IPC binding and got a ping/pong round trip going between JS and C++.Swapping the IPC layer out from under the frontend. The frontend was built entirely around
window.go.main.App.*Wails bindings. Instead of rewriting every call site, I wrotelib/ipc.ts(a typedinvoke<T>()/on()/off()client) andlib/wails-shim.ts, which intercepts the old Wails calls at runtime and routes them through the new IPC. That let the backend migrate one feature at a time without the frontend noticing. Terminal (ConPTY), file ops, config, search, sysinfo, sessions, packing, the updater, window management, clipboard, and shell ops all now go through C++.Making it feel native. A WebView in a default OS window looks cheap, so I added a frameless window with proper
WM_NCHITTESTdrag regions, ported the GDI+ splash screen and Windows jump list straight over from their Go implementations, and added a single-instance mutex so launching the app twice just refocuses the existing window.Cross-platform. Up to that point everything was Windows-only by necessity. Added a
forkpty-based terminal backend for macOS/Linux, made config paths resolve correctly per platform, guarded the_WIN32-only sysinfo code, and swapped WinHTTP for cpp-httplib plus OpenSSL so networking isn't Windows-locked either.The installer. Took longer than expected. Plan was to port the installer to C++/WebView too with the same IPC pattern, but I hit a string of rendering issues: blank screens from
file://CORS restrictions, a missingwindow.runtimestub crashing React on mount, temp paths with backslashes breakingfile://URL construction, and a frameless-window flash on launch. Fixed it by serving the frontend from a localcpp-httplibserver with virtual host mapping instead offile://, inlining SVG logos as data URIs, pre-creating the frameless HWND before showing it, and wrapping the shim import in a try/catch so React always mounts even if IPC isn't ready. End result: clean install flow with a GitHub-releases version picker, real progress events, no CMD-window flash, and everything statically linked.Tearing out the old build, CI, and Go itself. Rewrote
build.ps1from scratch as pure CMake with two variants (base and plugins, matching the old Wails artifact names so downstream tooling doesn't break). Then went throughcode-quality.yml,release.yml, andbuild-matrix.ymlfixing CI one failure at a time: vcpkg caching, rollup lockfile mismatches on Linux/macOS, cmake 4.x quirks on Ubuntu, a YAML heredoc indentation bug silently breakingrelease.yml, and removing Go from CodeQL/coverage. Once CI was green on all three platforms with no Go in the loop, deleted the Go application backend (78 files), the named-pipe IPC transport, and the standalonecmdide-backend.exetarget, then updated.gitignoreand the migration docs.129 Go/Wails files gone. No
.gofiles remain outside.git/.What else landed on this branch
cds into directories (with a "quick paths" banner), fixed Windows absolute-path extraction, fixed CWD label truncation, guarded against zero-dimension resize events that were panicking ConPTY during panel-split layout changes.darkandlight, with a follow-up contrast pass for dropdowns and the terminal that had hardcoded white text invisible on light backgrounds.anydowngrades, a curly-quote parse error, and a CodeQL alert about shell-string escaping in the ctrl-clickcdcommand. All clean now.Test plan
cmake --build cpp/build --config Release --target cmdide-hostsucceeds (CI: windows/macos/ubuntu all green)./build.ps1 -AppOnlyproducescmdIDE-windows-amd64.zip(base) andcmdIDE-plugins-windows-amd64.zip(plugins)cmdide-host.exeopens a window with the cmdIDE frontend UI.gofiles remain outside.git/app/frontend/